# coding=utf-8
#!/usr/bin/env python
#
# Copyright 2010 Felipe Hoffa
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import datetime
import logging
import math
from sets import Set

from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

from mapreduce import operation as op
from mapreduce import control
from mapreduce.lib.graphy.backends import google_chart_api

import markup
from markup import oneliner as e

import mapreduce

## different counters

tz = datetime.timedelta(hours=-4)

def stats_plays(entity):
    yield op.counters.Increment("%s-%02d" % (entity.draw_number, entity.previous_plays))

def stats_plays_daytime(entity):
    tzdatetime = entity.created_at + tz
    yield op.counters.Increment("%s-%s" % (tzdatetime.strftime('%Y/%m/%d'), tzdatetime.strftime('%H')))

def stats_plays_day(entity):
    tzdatetime = entity.created_at + tz
    yield op.counters.Increment("%s-%s" % (tzdatetime.strftime('%Y/%m/%d'), "%02d" % entity.previous_plays))

def stats_from_other_models(entity):
    import models
    other = models.OtherModel.get_by_uid(entity.uid)
    if not other:
        return
    yield op.counters.Increment("%s-%s" % ("%02d" % other.plays, "%02d" % other.vouchers))

## end counters

## internal models for datastore

class MrGraph(db.Model):
    created_at = db.DateTimeProperty(auto_now_add=True)
    updated_at = db.DateTimeProperty(auto_now=True)
    version = db.IntegerProperty(default=0)
    mr_key =  db.StringProperty()
    entity_kind = db.StringProperty()
    handler = db.StringProperty()
    
## end internal models

## webapp handlers

class List(webapp.RequestHandler):
    
    def get(self):
        graphs = MrGraph.all().order("-created_at").fetch(10)
        page = markup.page()
        page.init(title="Mr Graph")
        page.h3("Graphs")
        page.table()
        for graph in graphs:
            page.tr()
            page.td([e.a("%s" % (graph.mr_key), href="view?k=%s" % graph.mr_key), graph.created_at, graph.entity_kind, graph.handler])
            page.tr.close()
        page.table.close()
        page.h3("Actions")
        page.ul()
        page.li(e.a("start map reduce [%s] [%s]" % (Do.entity_kind, Do.handler), href="do"))
        page.ul.close()
        self.response.out.write(page)

class View(webapp.RequestHandler):

    def filter_cols(self, map, xlabels, col_labels):
        sumys = {}
        for i in col_labels:
            sumys[i] = sum([map[i].get(k, 0) for k in xlabels])
        col_max = max(sumys.values())
        col_labels = [y for y in col_labels if sumys[y] * 100 > col_max]
        return col_labels, col_max

    def make_chart(self, map, xlabels, col_labels, col_max):
        chart = google_chart_api.BarChart()
        chart.stacked = True
        chart.vertical = False
        chart.left.labels = col_labels
        chart.bottom.min = 0
        grid_spacing = max(1, 10 ** int((math.log(col_max, 10))) / 2)
        #chart.bottom.grid_spacing = grid_spacing
        chart.bottom.label_gridlines = True
        chart.bottom.labels = range(0, col_max+1, grid_spacing)
        chart.bottom.label_positions = chart.bottom.labels
        colors = ['4684EE', 'DC3912', 'FF9900', '008000', '666666', '4942CC', 'CB4AC5', 'D6AE00', '336699', 'DD4477']
        for i, k in enumerate(xlabels):
            chart.AddBars([map[y].get(k, None) for y in col_labels], label="%s" % k, color=colors[i % len(colors)])
        return chart

    def get(self):
        k = self.request.get("k")
        g = mapreduce.model.MapreduceState.get_by_key_name(k)
        counters = g.counters_map.counters
        if len(counters)<=1:
            self.response.out.write("not yet started")
            return
        page = markup.page()
        page.init(title="Mr Graph")
        page.style("td{text-align:right;}")
        
        map = {}
        xls = Set()
        for c, v in sorted(counters.items(), key=lambda t: t[0]):
            if "mapper_calls" == c:
                continue
            cs = c.split('-')
            if 1 == len(cs):
                cs.append('-)')
            values = map.setdefault(cs[0], {})
            values[cs[1]] = v
            xls.add(cs[1])
        xlabels = sorted(list(xls))
        col_labels = sorted(map.keys())

        col_labels, col_max = self.filter_cols(map, xlabels, col_labels)
                    
        chart= self.make_chart(map, xlabels, col_labels, col_max)
     
        page.img(src=chart.display.Url(500,600))
        page.table()
        page.tr()
        page.th(["-"] + ["%s" % k for k in xlabels])
        page.tr.close()
        for i in col_labels:
            page.tr()
            page.th(["%s" % i])
            page.td([map[i].get(k, None) for k in xlabels])
            page.tr.close()
        page.table.close()
        page.a("back", href="index")
        self.response.out.write(page)
        

class Do(webapp.RequestHandler):
    
    handler = "mr_graph.stats_plays_daytime"
    entity_kind = "models.LotoPlay"

    def get(self):
        mr_key = control.start_map(
            name="graph", 
            handler_spec=Do.handler, 
            reader_spec="mapreduce.input_readers.DatastoreInputReader", 
            reader_parameters={"entity_kind":Do.entity_kind},
            shard_count=1,
            mapreduce_parameters={}, 
        )
        MrGraph(mr_key=mr_key, handler=Do.handler, entity_kind=Do.entity_kind).put()
        page = markup.page()
        page.init(title="Mr Graph")
        page.p("[started %s]" % mr_key )
        page.a("back", href="index")
        self.response.out.write(page)

def application():        
    application = webapp.WSGIApplication([
        ('.*/', List),
        ('.*/index', List),        
        ('.*/do', Do),
        ('.*/view', View),
    ], debug=True)
    return application


def main():
    run_wsgi_app(application())

if __name__ == "__main__":
    main()

## end webapp handlers